/*
 *  Copyright 2013 HCIWriter
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.hci.writer.server;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.hci.writer.client.StorageService;
import com.hci.writer.shared.Participant;
import com.hci.writer.shared.Task;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class StorageServiceImpl extends RemoteServiceServlet implements
		StorageService {

	@Override
	public boolean store(Participant p) throws IOException {
		DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
		
		try {
			Key parentKey = KeyFactory.createKey("Participant", p.getKey());
			Entity entity = new Entity(parentKey);
			entity.setProperty("Key", p.getKey());
			entity.setProperty("NumTasks", (Integer)p.getNumberOfTasks());
			datastoreService.put(entity);
			
			for(int i = 0; i < p.getNumberOfTasks(); ++i) {
				entity = new Entity("Task", String.valueOf(i), parentKey);
				entity.setProperty("blob", serialize(p.getTask(i)));
				datastoreService.put(entity);
			}
			
		} catch (RuntimeException e) {
			e.printStackTrace();
			throw e;
		}
		
		return true;
	}
	
	@Override
	public ArrayList<String> getStoredParticipants() {
		DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
		
		Query q = new Query("Participant").setKeysOnly();
		PreparedQuery pq = datastoreService.prepare(q);
		
		ArrayList<String> result = new ArrayList<String>();
		for (Entity e : pq.asIterable()) {
			result.add(e.getKey().getName());
		}
		
		return result;
	}
	
	@Override
	public	Participant getParticipant(String key) throws IOException {
		DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
		Key parentKey = KeyFactory.createKey("Participant", key);
		try {
			Entity entity = datastoreService.get(parentKey);
			Participant participant = new Participant(key);
			final int numTasks = ((Long)entity.getProperty("NumTasks")).intValue();
			for(int i = 0; i < numTasks; ++i) {
				entity = datastoreService.get(KeyFactory.createKey(parentKey, "Task", String.valueOf(i)));
				Blob blob = (Blob) entity.getProperty("blob");
				participant.addTask(unserializeTask(blob));
			}

			return participant;
		} catch (EntityNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	private Blob serialize(Task t) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutput out = null;
		try {
			out = new ObjectOutputStream(bos);
			out.writeObject(t);
			return new Blob(bos.toByteArray());
		} finally {
			if(out != null) {
				out.close();
			}
			bos.close();
		}
	}
	
	private Task unserializeTask(Blob b) throws IOException {
		ByteArrayInputStream bis = new ByteArrayInputStream(b.getBytes());
		ObjectInput in = null;
		try {
			in = new ObjectInputStream(bis);
			try {
				return (Task) in.readObject();
			} catch (ClassNotFoundException e) {
				return null;
			}
		} finally {
			bis.close();
			in.close();
		}
	}
}
